package com.leyou.item.service;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.leyou.common.constants.MQConstants;
import com.leyou.common.exception.pojo.ExceptionEnum;
import com.leyou.common.exception.pojo.LyException;
import com.leyou.common.pojo.PageResult;
import com.leyou.common.utils.BeanHelper;
import com.leyou.common.utils.JsonUtils;
import com.leyou.item.dto.SpuDTO;
import com.leyou.item.mapper.FavoriteMapper;
import com.leyou.item.mapper.SkuMapper;
import com.leyou.item.mapper.SpuDetailMapper;
import com.leyou.item.mapper.SpuMapper;
import com.leyou.item.pojo.*;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 商品的Service
 */
@Service
@Transactional
public class GoodsService extends ServiceImpl<SkuMapper, Sku> {
    @Autowired
    private SpuMapper spuMapper;
    @Autowired
    private SpuDetailMapper spuDetailMapper;
    @Autowired
    private SkuMapper skuMapper;
    @Autowired
    private BrandService brandService;
    @Autowired
    private CategoryService categoryService;
    @Autowired
    private AmqpTemplate amqpTemplate;
    @Autowired
    private SpecService specService;

    public PageResult<SpuDTO> findSpuByPage(Integer page, Integer rows, String key, Boolean saleable) {
        //1.封装查询条件

        //1.1 封装分页参数
        IPage<Spu> iPage = new Page(page, rows);

        //1.2 封装条件参数
        QueryWrapper<Spu> queryWrapper = Wrappers.query();

        //1）处理key关键词
        //需求：匹配name 和 sub_title字段
        //sql： where (name like %key% or sub_title like %key% )
        if (StringUtils.isNotBlank(key)) {
            queryWrapper.and(i -> i.
                    like("name", key)
                    .or()
                    .like("sub_title", key));
        }

        //2）处理saleable
        //sql: saleable=true/false
        if (saleable != null) {
            queryWrapper.eq("saleable", saleable);
        }


        //2.执行查询，获取分页结果
        iPage = spuMapper.selectPage(iPage, queryWrapper);

        //3.处理结果（封装PageResult）
        //3.1 把iPage中的Spu集合 转换为 SpuDTO集合
        List<Spu> records = iPage.getRecords();
        //3.2 把Spu数据拷贝到SpuDTO
        List<SpuDTO> spuDTOS = BeanHelper.copyWithCollection(records, SpuDTO.class);
        //3.3 填充SpuDTO的分类名称和品牌名称
        getCategoryNameAndBrandName(spuDTOS);
        //3.4 封装PageResult对象
        PageResult<SpuDTO> pageResult = new PageResult<>(iPage.getTotal(), iPage.getPages(), spuDTOS);

        //4.返回结果
        return pageResult;
    }

    /**
     * 封装分类名称和品牌名称
     *
     * @param spuDTOS
     */
    private void getCategoryNameAndBrandName(List<SpuDTO> spuDTOS) {
        //1.遍历集合
        spuDTOS.forEach(spuDTO -> {
            //2.查询品牌
            Brand brand = brandService.findBrandById(spuDTO.getBrandId());
            spuDTO.setBrandName(brand.getName());

            //3.查询分类集合
            List<Category> categories = categoryService.findCategoriesByIds(Arrays.asList(spuDTO.getCid1(), spuDTO.getCid2(), spuDTO.getCid3()));
            //格式： 手机通讯/手机/手机
            //stream流式编程
            /**
             * 作用： 简化对集合（List，Map，Set）的操作
             * 核心方法：
             *     stream(): 把集合转换流
             *     map(): 映射的方法， 对集合的元素怎么处理？ 例如：取出List<Category>的Category的name属性值
             *     collect： 收集（归总）的方法， 对map处理完的结果进行归总。 例如：把map处理完的所有name属性，再次封装成List集合,List<String>
             */
            //.map(category -> category.getName()) 语法的简化版本： .map(Category::getName)

            /**
             * Collectors.toList(): 把结果收集成List集合
             * Collectors.toSet()： 把结果收集成Set集合
             * Collectors.toMap(): 把结果收集成Map集合
             * Collectors.joining("/")： 把结果收集成以指定分隔符拼接的字符串
             */
            String categoryName = categories.stream().map(Category::getName).collect(Collectors.joining("/"));

            spuDTO.setCategoryName(categoryName);
        });
    }

    /**
     * 保存商品
     *
     * @param spuDTO
     */
    public void saveGoods(SpuDTO spuDTO) {
        try {
            //把SpuDTO转为Spu对象
            Spu spu = BeanHelper.copyProperties(spuDTO, Spu.class);
            //设置为默认下架
            spu.setSaleable(false);

            //1.保存Spu表信息
            spuMapper.insert(spu);//注意：MyBatis-Plus会自动在insert方法之后，获取数据库自增值并赋值给主键属性

            //2.保存SpuDetail信息
            SpuDetail spuDetail = spuDTO.getSpuDetail();
            spuDetail.setSpuId(spu.getId());
            spuDetailMapper.insert(spuDetail);

            //3.保存Skus信息
            List<Sku> skus = spuDTO.getSkus();
            //设置spuId
            skus.forEach(sku -> {
                sku.setSpuId(spu.getId());
            });
            //使用MyBatis-Plus批量插入
            saveBatch(skus);
        } catch (Exception e) {
            e.printStackTrace();
            throw new LyException(ExceptionEnum.INSERT_OPERATION_FAIL);
        }
    }

    /**
     * 上下架
     *
     * @param id
     * @param saleable
     */
    public void updateSaleable(Long id, Boolean saleable) {

        try {
            Spu spu = new Spu();
            spu.setId(id);
            spu.setSaleable(saleable);

            spuMapper.updateById(spu); // updateById: 自动判断对象的属性为NULL，为null的属性则不更新

            //根据上下架操作发送不同类型的消息
            String routingKey = saleable ? MQConstants.RoutingKey.ITEM_UP_KEY : MQConstants.RoutingKey.ITEM_DOWN_KEY;

            amqpTemplate.convertAndSend(MQConstants.Exchange.ITEM_EXCHANGE_NAME, routingKey, id);

        } catch (Exception e) {
            e.printStackTrace();
            throw new LyException(ExceptionEnum.UPDATE_OPERATION_FAIL);
        }
    }

    /**
     * 根据spu id 找出所有的sku
     *
     * @param id
     * @return
     */
    public List<Sku> findSkusBySpuId(Long id) {
        Sku sku = new Sku();
        sku.setSpuId(id);

        QueryWrapper<Sku> queryWrapper = Wrappers.query(sku);

        List<Sku> skus = skuMapper.selectList(queryWrapper);

        if (CollectionUtils.isEmpty(skus)) {
            throw new LyException(ExceptionEnum.GOODS_NOT_FOUND);
        }
        this.OwnSpecNumToString(skus);
        return skus;
    }

    /**
     * 商品ID查详情
     *
     * @param id
     * @return
     */
    public SpuDetail findSpuDetialBySpuId(Long id) {
        SpuDetail spuDetail = spuDetailMapper.selectById(id);
        if (spuDetail == null) {
            throw new LyException(ExceptionEnum.GOODS_NOT_FOUND);
        }
        return spuDetail;
    }

    /**
     * 根据Spu id  封装为SpuDto
     *
     * @param id
     * @return
     */
    public SpuDTO findSpuById(Long id) {
        try {
            //1.查询Spu对象
            Spu spu = spuMapper.selectById(id);
            //2.拷贝数据到SpuDTO对象中
            SpuDTO spuDTO = BeanHelper.copyProperties(spu, SpuDTO.class);
            //3.查询并封装品牌对象
            Brand brand = brandService.findBrandById(spuDTO.getBrandId());
            spuDTO.setBrandName(brand.getName());
            //4.查询并封装分类名称
            List<Category> categoryList = categoryService.findCategoriesByIds(Arrays.asList(spuDTO.getCid1(), spuDTO.getCid2(), spuDTO.getCid3()));
            String categoryName = categoryList.stream().map(Category::getName).collect(Collectors.joining("/"));
            spuDTO.setCategoryName(categoryName);
            //5.查询并封装SpuDetail对象
            SpuDetail spuDetail = this.findSpuDetialBySpuId(id);
            spuDTO.setSpuDetail(spuDetail);
            //6.查询所有Sku并封装
            List<Sku> skus = this.findSkusBySpuId(id);
            spuDTO.setSkus(skus);
            return spuDTO;
        } catch (Exception e) {
            e.printStackTrace();
            throw new LyException(ExceptionEnum.GOODS_NOT_FOUND);
        }
    }

    /**
     * Find Sku By Sku Ids
     *
     * @param ids
     * @return
     */
    public List<Sku> findSkusByIds(List<Long> ids) {
        List<Sku> skus = skuMapper.selectBatchIds(ids);
        if (CollectionUtils.isEmpty(skus)) {
            throw new LyException(ExceptionEnum.GOODS_NOT_FOUND);
        }
        return skus;
    }

    /**
     * 减库存
     *
     * @param paramMap
     */
    public void minusStock(Map<Long, Integer> paramMap) {
        //1.遍历所有商品
        paramMap.entrySet().forEach(entry -> {
            //2.取出skuId和num
            Long skuId = entry.getKey();
            Integer num = entry.getValue();

            //3.先查询Sku对象
            Sku sku = skuMapper.selectById(skuId);

            //判断原来的库存量大于等于num
            if (sku.getStock() >= num) {
                //4.修改库存量
                sku.setStock(sku.getStock() - num);
                //5.更新Sku对象
                skuMapper.updateById(sku);
            } else {
                throw new LyException(ExceptionEnum.STOCK_NOT_ENOUGH_ERROR);
            }
        });

    }

    public void plusStock(Map<Long, Integer> paramMap) {
        //1.遍历所有商品
        paramMap.entrySet().forEach(entry -> {
            //2.取出skuId和num
            Long skuId = entry.getKey();
            Integer num = entry.getValue();

            //3.先查询Sku对象
            Sku sku = skuMapper.selectById(skuId);

            //4.修改库存量
            sku.setStock(sku.getStock() + num);
            //5.更新Sku对象
            skuMapper.updateById(sku);

        });
    }

    public void checkStock(Map<Long, Integer> resultMap) {
        //1.遍历所有商品
        resultMap.entrySet().forEach(entry -> {
            //2.取出skuId和num
            Long skuId = entry.getKey();
            Integer num = entry.getValue();

            //3.先查询Sku对象
            Sku sku = skuMapper.selectById(skuId);

            //判断原来的库存量大于等于num
            if (sku.getStock() < num) {
                throw new LyException(ExceptionEnum.STOCK_NOT_ENOUGH_ERROR);
            }
        });
    }

    @Autowired
    private FavoriteMapper favoriteMapper;

    /**
     * 康康是不是已经收藏了   否则收藏
     *
     * @param skuid
     * @return
     */
    public Integer favoriteGoods(Long skuid, Long userid) {

        QueryWrapper<Favorite> rapper = new QueryWrapper<Favorite>().eq("sku_id", skuid).eq("user_id", userid);
        Integer integer = favoriteMapper.selectCount(rapper);
        if (integer > 0) {
            return 1;
        }

        favoriteMapper.insert(new Favorite(skuid, userid));
        return 0;
    }

    public List<Sku> findFavoriteSku(Long id) {
        List<Favorite> userFavorite = favoriteMapper.selectList(new QueryWrapper<Favorite>().eq("user_id", id));
        //所有skuid处理成list集合
        List<Long> skuIds = userFavorite.stream().map(Favorite::getSkuId).collect(Collectors.toList());
        //查出所有sku
        List<Sku> skuList = listByIds(skuIds);
        //遍历
        OwnSpecNumToString(skuList);

        return skuList;
    }

    /**
     *
     * @param skuList
     */
    public List<Sku> OwnSpecNumToString(List<Sku> skuList) {
        skuList.forEach(sku -> {
            //sku参数:OwnSpec转换成map::Json to Map
            Map<Long, String> map = JsonUtils.toMap(sku.getOwnSpec(), Long.class, String.class);

            //取出map集合所有key查询SpecParam表
            List<SpecParam> skuownSpecList = specService.findSpecParamById(map.keySet());
            //处理成map key = SpecParam.name      value = SpecParam.id
            Map<String, Long> SpecMap = skuownSpecList.stream().collect(Collectors.toMap(SpecParam::getName, SpecParam::getId));
            //一个新的map
            HashMap<String, String> RedayMap = new HashMap<>();

            SpecMap.forEach((k, v) -> {
                String value = map.get(v);
                RedayMap.put(k, value);
            });
            sku.setOwnSpec(JsonUtils.toString(RedayMap));
        });
        return skuList;
    }

    public Sku findSkuById(Long id){
        return skuMapper.selectById(id);
    }
}
